package com.storyroad.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.json.JSONException;
import org.json.JSONObject;

import com.storyroad.utils.DBConnection;
import com.storyroad.utils.TableNames;
import com.storyroad.utils.Utilities;

/**
 * Servlet implementation class Login
 */
// @WebServlet("/LoginServlet")
public class Login extends HttpServlet {
	private static final long serialVersionUID = 1L;

	/**
	 * @see HttpServlet#HttpServlet()
	 */
	public Login() {
		super();
		// TODO Auto-generated constructor stub
	}

	/**
	 * Carries out the login operation. Registered users can successfully log on to the system only if they provide the correct
	 * credentials (e-mail and password) and their account is not banned at the time of the login attempt. If it is the
	 * admin who is trying to log on to the system, the admin console is brought up in the front-end after successful
	 * login.
	 * @param request HttpServletRequest object that contains data coming from front-end
	 * @param response HttpServletResponse object that contains data which is prepared after processing the incoming data
	 * @throws ServletException
	 * @throws IOException
	 */
	protected void processRequest(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		boolean isBannedUser;
		String f_email = request.getParameter("email");
		String f_password = request.getParameter("password");
		String f_requestType = request.getParameter("request_type");
		Connection conn = new DBConnection().connect();
		PrintWriter out = response.getWriter();

		// Check whether the login attempt is made by the admin
		if (f_email.equals("admin")) {
			adminLogin(request, response, f_email, f_password, conn, out);
			return;
		}

		boolean loginSuccess;
		if (conn == null) {
			out.println("Connection Error");
		} else {
			try {
				String hashedPassword = Utilities.hashPassword(f_password);
				PreparedStatement stmt = conn.prepareStatement("SELECT username FROM " + TableNames.USERS_TABLE + " WHERE email = ? AND password = ?");
				stmt.setString(1, f_email);
				stmt.setString(2, hashedPassword);
				ResultSet rs = stmt.executeQuery();
				HttpSession session = request.getSession();
				request.setCharacterEncoding("utf8");
				response.setContentType("application/json");

				if (rs.next()) {
					String username = rs.getString("username");
					// Check whether this user is banned
					isBannedUser = isBannedUser(conn, username);
					if(!isBannedUser){
						// If credentials are valid and the user is not banned, login is successful. Create a session for this user.
						session.setAttribute("username", username);
						session.setMaxInactiveInterval(30 * 60);
						Cookie loginCookie = new Cookie("username", username);
						loginCookie.setMaxAge(30 * 60);
						response.addCookie(loginCookie);
						loginSuccess = true;						
					}else{
						// Credentials are correct but the account is banned. Return an error message.
						request.setAttribute("username", "Ban");
						loginSuccess = false;
					}
				} else {
					// Credentials provided by the user are not valid. Return an error message.
					request.setAttribute("username", "Fail");
					loginSuccess = false;
				}

				if (f_requestType.equals("Android")) {
					JSONObject loginSuccessJsonObject = new JSONObject();
					try {
						loginSuccessJsonObject.put("loginSuccess", loginSuccess);
					} catch (JSONException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					out.print(loginSuccessJsonObject.toString());
				} else if (f_requestType.equals("Web")) {
					request.getRequestDispatcher("LoginPage.jsp").forward(request, response);
				}
			} catch (SQLException e) {
				out.print(e.getMessage());
				e.printStackTrace();
			}
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	/**
	 * Based on the given credentials, carries out the login operation for the admin user. If the provided credentials are
	 * correct, login will be successful and the user will be redirected to the admin console.
	 * @param request HttpServletRequest object that contains data coming from front-end
	 * @param response HttpServletResponse object that contains data which is prepared after processing the incoming data
	 * @param name name of the admin user
	 * @param pass password of the admin user
	 * @param conn connection object to communicate with the database
	 * @param out PrintWriter object to print out error messages when needed
	 * @throws ServletException
	 * @throws IOException
	 */
	protected void adminLogin(HttpServletRequest request,
			HttpServletResponse response, String name, String pass,
			Connection conn, PrintWriter out) throws ServletException,
			IOException {

		if (conn == null) {
			out.println("Connection Error");
		} else {
			try {
				String hashedPassword = Utilities.hashPassword(pass);
				PreparedStatement stmt = conn
						.prepareStatement("SELECT username FROM "
								+ TableNames.ADMIN_TABLE
								+ " WHERE username = ? AND password = ?");
				stmt.setString(1, name);
				stmt.setString(2, hashedPassword);
				ResultSet rs = stmt.executeQuery();
				HttpSession session = request.getSession();

				request.setCharacterEncoding("utf8");
				response.setContentType("application/json");

				if (rs.next()) {
					String username = rs.getString("username");
					session.setAttribute("username", username);
					session.setMaxInactiveInterval(30 * 60);
					Cookie loginCookie = new Cookie("username", username);
					loginCookie.setMaxAge(30 * 60);
					response.addCookie(loginCookie);
				} else {
					request.setAttribute("username", "Fail");
					request.getRequestDispatcher("LoginPage.jsp").forward(request, response);
					return;
				}
				request.getRequestDispatcher("Admin.jsp").forward(request,response);
			} catch (SQLException e) {
				out.print(e.getMessage());
				e.printStackTrace();
			}
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	/**
	 * Check whether the user who is attempting to log on to the system is currently banned from the system.
	 * @param conn connection object to communicate with the database
	 * @param username username of the user who is trying to log on to the system
	 * @return @c true if the user is currently banned, @c false otherwise
	 */
	protected boolean isBannedUser(Connection conn, String username){
		boolean isBannedUser = false;
		ResultSet rs;
		try {
			PreparedStatement stmt = conn.prepareStatement("SELECT date FROM " + TableNames.BANNED_USER_TABLE + " WHERE username = ?");
			stmt.setString(1, username);
			rs = stmt.executeQuery();
			if(rs.next()){
				// User account is in the Ban table. Check whether the ban has expired yet.
				Timestamp date;
				date = rs.getTimestamp("date");
				isBannedUser = checkBanStatus(conn, date);
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return isBannedUser;
	}
	
	/**
	 * Checks whether the ban of a user has expired
	 * @param conn connection object to communicate with the database
	 * @param date current time
	 * @return @c true if the ban on the account has not expired yet, @c false otherwise
	 */
	protected boolean checkBanStatus(Connection conn, Timestamp date){
		boolean isBannedUser = false;
		int result;
		java.util.Date today;
		Timestamp timestampNow;
		today = new java.util.Date();
		timestampNow = new Timestamp(today.getTime());
		result = timestampNow.compareTo(date);
		// If current time is bigger than the time in the Ban table then the ban on this user has expired.
		if(result >= 0){
			try {
				// Remove this user from the banned users list.
				PreparedStatement stmt = conn.prepareStatement("DELETE FROM " + TableNames.BANNED_USER_TABLE + " WHERE date = ?");
				stmt.setTimestamp(1, date);
				stmt.executeUpdate();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}else{
			// Ban on this user has not expired yet.
			isBannedUser = true;
		}
		return isBannedUser;
	}
	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		processRequest(request, response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		processRequest(request, response);
	}

}
